#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <ctime>
#include <unordered_map>
#include <sstream>
#include <functional>
#include <fstream>
#include "Log.hpp"
#include "cookieOrsession.hpp"
using namespace std;
// 协议就是一个结构化字段
// 本服务忽略TCP粘包问题
static const string sep = "\r\n";
static const string header_sep = ": ";
static const string wwwroot = "wwwroot";
static const string homepage = "index.html";
static const string httpversion = "HTTP/1.0";
static const string space = " ";
static const string filesuffixsep = ".";
static const string args_sep = "?";
class HttpRequest;
class HttpResponse;
using func_t = function<shared_ptr<HttpResponse>(shared_ptr<HttpRequest>)>; // 服务执行的方法
int nums = 0;
// Http请求
class HttpRequest
{
private:
    // 拿到一行有效信息
    string GetOneLine(string &reqstr)
    {
        if (reqstr.empty() == true)
        {
            return reqstr;
        }
        auto pos = reqstr.find(sep); // 拿到下标
        if (pos == string::npos)
        {
            return string();
        }
        string line = reqstr.substr(0, pos); // 截取有效信息
        reqstr.erase(0, pos + sep.size());   // 删除已经提取的信息
        // 如果line是空串，就说明请求报头已经被截取完了，返回\r\n即可
        if (line.empty() == true)
        {
            return sep;
        }
        return line;
    }
    // 将line字符串按照": "分为kv结构，分别传递给k,v
    bool ParseHeaderHelper(const string &line, string *k, string *v)
    {
        auto pos = line.find(header_sep);
        if (pos == string::npos)
        {
            return false;
        }
        *k = line.substr(0, pos);
        *v = line.substr(pos + header_sep.size(), line.size());
        return true;
    }

public:
    HttpRequest()
        : _blank_line(sep), _path(wwwroot)
    {
    }

    // 序列化
    void Serialize() {}
    // 反序列化
    void Derialize(string &reqstr)
    {
        _req_line = GetOneLine(reqstr);
        while (true)
        {
            string line = GetOneLine(reqstr);
            if (line.empty() == true)
            {
                break;
            }
            else if (line == sep)
            {
                // 读到空行了,正文部分就是剩下的reqstr
                _req_text = reqstr;
                break;
            }
            else
            {
                // 是一行请求报文
                _req_header.emplace_back(line);
            }
        }
        ParseReqLine();
        ParseHeader();
    }

    // 测试
    void Print()
    {
        cout << "===" << _req_line << endl;
        for (auto header : _req_header)
        {
            cout << "***" << header << endl;
        }
        cout << _blank_line;
        cout << _req_text;

        cout << "method ### " << _method << endl;
        cout << "url ### " << _url << endl;
        cout << "path ### " << _path << endl;
        cout << "httpversion ### " << _version << endl;
        for (auto &header : _headers)
        {
            cout << "@@@" << header.first << " - " << header.second << endl;
        }
    }
    // 完善_method、_url、_version属性
    bool ParseReqLine()
    {
        if (_req_line.empty() == true)
        {
            return false;
        }
        // 将_req_line按照空格进行打散，分给_method、_url、_version
        stringstream ss(_req_line);
        ss >> _method >> _url >> _version;

        //_url:/index.html?name=zhangsan&password=123456---get
        if (strcasecmp("get", _method.c_str()) == 0) // 忽略大小写，字符串相同判断
        {
            auto pos = _url.find(args_sep);
            if (pos != string::npos)
            {
                // 找到了位置
                LOG(INFO, "change begin,url:%s\n", _url.c_str());
                _args = _url.substr(pos + args_sep.size(), _url.size()); // 将参数写入_args
                _url.resize(pos);                                        // 将参数部分与url剔除
                LOG(INFO, "change done,url:%s , args:%s\n", _url.c_str(), _args.c_str());
            }
        }

        _path += _url;

        LOG(DEBUG, "url:%s\n", _url.c_str());

        // 判断是不是请求的/,拼接首页
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }
        // 处理文件后缀
        auto pos = _path.rfind(filesuffixsep);
        if (pos == string::npos)
        {
            _suffix = ".unknown"; // 默认后缀
        }
        else
        {
            _suffix = _path.substr(pos, _path.size());
        }
        LOG(INFO, "client wang get %s,suffix:%s\n", _path.c_str(), _suffix.c_str());
        return true;
    }
    // 将vector请求报文放在kv结构-请求报文中
    bool ParseHeader()
    {
        for (auto &header : _req_header)
        {
            string k, v;
            bool tmp = ParseHeaderHelper(header, &k, &v);
            if (tmp == true)
            {
                _headers.insert(make_pair(k, v));
            }
        }
        return true;
    }
    // 检查请求信息kv结构中是否有Cookie session=xxx,如果有，返回sessionid，没有 string()
    string GetSessionid()
    {
        auto pos = _headers.find("Cookie");
        if (pos == _headers.end())
        {
            // 没有Cookie信息
            return string();
        }
        string session = _headers["Cookie"];
        // session=xxx
        auto pos1 = session.find("=");
        if (pos1 == string::npos)
        {
            return string();
        }
        string sessionid = session.substr(pos1 + 1, session.size());
        if (sessionid.empty() == true)
        {
            return string();
        }
        return sessionid;
    }

    string Path()
    {
        return _path;
    }
    // 获取客户端要访问资源的文件后缀
    string Suffix()
    {
        return _suffix;
    }
    string Url()
    {
        return _url;
    }
    // 判断正文部分或者参数部分有内容-表示需要进行登录
    bool isExec()
    {
        if ((_args.empty() == false) || (_req_text.empty() == false))
        {
            return true;
        }
        return false;
    }
    string Method()
    {
        return _method;
    }
    string Text()
    {
        return _req_text;
    }
    string Args()
    {
        return _args;
    }
    ~HttpRequest() {}

private:
    string _req_line;           // 请求行
    vector<string> _req_header; // 请求报文
    string _blank_line;         // 空行
    string _req_text;           // 请求正文
    string _args;               // 客户端使用get提交到url的参数

    string _method;                         // 请求方法
    string _url;                            // url
    string _path;                           // 资源
    string _version;                        // HTTP版本
    unordered_map<string, string> _headers; // kv结构-请求报文
    string _suffix;                         // 文件后缀
    string _sessionid;                      // sessionid
};

// Http应答
class HttpResponse
{
public:
    HttpResponse()
        : _version(httpversion), _blank_line(sep)
    {
    }
    // 添加状态行
    void AddStatusLine(int code, const string desc)
    {
        _code = code; // 状态码直接设置200
        _desc = desc;
    }
    // 添加响应报头的属性
    void AddHeader(const string &k, const string &v)
    {
        LOG(DEBUG, "AddHeader %s->%s\n", k.c_str(), v.c_str());
        _headers[k] = v;
    }
    // 添加正文部分
    void AddText(const string &text)
    {
        _resp_text = text;
    }
    // 序列化
    string Serialize()
    {
        // 构建状态行
        _status_line = _version + space + to_string(_code) + space + _desc + sep;
        // 构建应答报文
        for (auto &header : _headers)
        {
            _resp_header.emplace_back(header.first + header_sep + header.second + sep);
        }
        // 序列化
        string respstr = _status_line;
        for (auto &header : _resp_header)
        {
            respstr += header;
        }
        respstr += _blank_line;
        respstr += _resp_text;
        return respstr;
    }

    ~HttpResponse() {}

private:
    // 应答的必要字段
    string _version; // HTTP版本
    int _code;       // 状态码
    string _desc;    // 状态码描述

    // 应答的结构化字段
    string _status_line;                    // 状态行
    vector<string> _resp_header;            // 应答报文
    string _blank_line;                     // 空行
    string _resp_text;                      // 应答正文
    unordered_map<string, string> _headers; // 应答报文kv结构
};

// 工厂
class Factor
{
public:
    // 获取一个请求对象指针
    static shared_ptr<HttpRequest> BuildHttpRequest()
    {
        return make_shared<HttpRequest>();
    }
    // 获取一个响应对象指针
    static shared_ptr<HttpResponse> BuildHttpResponse()
    {
        return make_shared<HttpResponse>();
    }
};

// Http服务
class HttpServer
{
public:
    HttpServer()
    {
        _mime_type.insert(make_pair(".html", "text/html"));
        _mime_type.insert(make_pair(".css", "text/css"));
        _mime_type.insert(make_pair(".js", "application/x-javascript"));
        _mime_type.insert(make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(make_pair(".png", "image/png"));
        _mime_type.insert(make_pair(".unknown", "text/html"));

        _code_to_desc.insert(make_pair(100, "Continue"));
        _code_to_desc.insert(make_pair(200, "OK"));
        _code_to_desc.insert(make_pair(301, "Moved Permanently"));
        _code_to_desc.insert(make_pair(302, "Found"));
        _code_to_desc.insert(make_pair(404, "Not Found"));
        _code_to_desc.insert(make_pair(100, "Internal Server Error"));
    }
    ~HttpServer() {}
    // 获取文件内容
    string ReadFileContent(const string &path, int *size)
    {
        // 将文件按照二进制打开
        ifstream in(path, std::ios::binary);
        if (in.is_open() == false)
        {
            return string();
        }
        // 将文件读写位置定位到文件结尾
        in.seekg(0, in.end);
        // 获取此时文件读写位置的偏移量，就获取到了此文件的大小
        int filesize = in.tellg();
        // 将文件的读写位置恢复到文件开始
        in.seekg(0, in.beg);
        string content;
        content.resize(filesize);                   // 提前开好空间
        in.read((char *)content.c_str(), filesize); // 读取文件内容到content
        in.close();
        *size = filesize; // 将文件大小带出去
        return content;
    }
    // 给方法集合中添加，url对应的方法
    void AddHandler(const string functionname, func_t f)
    {
        _funcs[functionname] = f;
    }
    string GetMonthName(int month)
    {
        vector<string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return months[month];
    }
    string GetWeekDayName(int day)
    {
        vector<string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return weekdays[day];
    }
    string ExpireTimeUseRfc1123(int t)
    {
        time_t timeout = time(nullptr) + t;
        struct tm *tm = gmtime(&timeout);
        char timebuffer[1024];
        snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC",
                 GetMonthName(tm->tm_wday).c_str(),
                 tm->tm_mday,
                 GetMonthName(tm->tm_mon).c_str(),
                 tm->tm_year + 1900,
                 tm->tm_hour,
                 tm->tm_min,
                 tm->tm_sec);
        return timebuffer;
    }

    // #define TEST 1
    string HandlerHttpRequest(string req)
    {
#ifdef TEST
        cout << "-------------------------------" << endl;
        cout << req;
        string response = "HTTP/1.0 200 OK\r\n";
        response += "\r\n";
        response += "<html><body><h1>Hello World</h1></body></html>";
        return response;
#else
        cout << "-------------------------------" << endl;
        cout << req;
        // 服务器收到报文req
        //  1.获取一个请求对象指针
        shared_ptr<HttpRequest> request = Factor::BuildHttpRequest();
        // 将此报文每个部分拆分为request对象中的属性值，以便更好的处理
        //  2.反序列化
        request->Derialize(req);

        // 3.拿到客户端想要访问的文件的二进制文件内容
        int contentsize = 0; // 二进制文件大小
        string text = ReadFileContent(request->Path(), &contentsize);

        // 获取要访问资源的文件后缀
        string suffix = request->Suffix();

        // 构建应答
        shared_ptr<HttpResponse> response = Factor::BuildHttpResponse();

        // 为应答添加信息
        // 添加状态行
        int code = 200;
        // 重定向测试
        if (request->Url() == "/redirect")
        {
            code = 302;
            string newurl = "http://bilibili.com";
            response->AddHeader("Location", newurl);
        }
        // 404测试
        if (text.empty() == true && request->Url() != "/Login")
        {
            code = 404;
            text = ReadFileContent("wwwroot/404.html", &contentsize);
        }
        // 登录-执行指定方法
        if (request->isExec() == true)
        {
            _funcs[request->Url()](request); // 处理业务---此时我不接收
            // 登录-执行指定方法，返回一个醒目的结果显示
            text = ReadFileContent("wwwroot/success_get_data.html", &contentsize);
        }
        // session测试
        if (request->Url() == "/session")
        {
            // 在request中拿到sessionid
            string sessionid = request->GetSessionid();
            if (sessionid.empty() == true)
            {
                // 首次创建，服务器为其创建session
                shared_ptr<Session> session(new Session());
                // 将session对象写入到管理session对象的哈希表中
                string sessionid = _mas.GetSessionid(session);
                // 拿到了sessionid
                // 向客户端中写入sessionid
                response->AddHeader("Set-Cookie", "session=" + sessionid);
                // 此时，客户端就有sessionid了
                LOG(DEBUG, "=======================================\n");
                LOG(DEBUG, "creat a session user , sessionid:%s\n", sessionid.c_str());
                LOG(DEBUG, "=======================================\n");
            }
            else
            {
                // 老session用户，直接拿数据
                shared_ptr<Session> session = _mas.GetSession(sessionid);
                // 打印消息
                LOG(DEBUG, "=======================================\n");
                LOG(DEBUG, "session user is running, sessionid:%s\n", sessionid.c_str());
                LOG(DEBUG, "username:%s , password:%s\n", session->_username, session->_password);
                LOG(DEBUG, "=======================================\n");
            }
        }
        response->AddStatusLine(code, _code_to_desc[code]);
        // 添加报头属性
        response->AddHeader("Content-Length", to_string(contentsize));
        response->AddHeader("Content-Type", _mime_type[suffix]);

        // 测试cookie
        if (request->Url() == "/cookie")
        {
            // string my_cookie = to_string(nums++) + "-username=zhangsan; expires=" + ExpireTimeUseRfc1123(60) + ";"; // 每次都加一个编号
            string my_cookie = to_string(nums++) + "-username=zhangsan; path=/cookie";
            response->AddHeader("Set-Cookie", my_cookie);
        }

        // 添加正文
        response->AddText(text);

        // 序列化
        string responsestr = response->Serialize();
        return responsestr;
#endif
    }

private:
    unordered_map<string, string> _mime_type; // Content_Type类型与文件后缀对照表
    unordered_map<int, string> _code_to_desc; // 状态码-状态描述
    unordered_map<string, func_t> _funcs;     // 方法集合-url->方法
    manageSession _mas;                       // 管理session对象指针与sessionid映射的类
};